1 /* 2 * Copyright (C) 2008 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.base; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 import static com.google.common.base.Preconditions.checkState; 21 import static java.util.concurrent.TimeUnit.DAYS; 22 import static java.util.concurrent.TimeUnit.HOURS; 23 import static java.util.concurrent.TimeUnit.MICROSECONDS; 24 import static java.util.concurrent.TimeUnit.MILLISECONDS; 25 import static java.util.concurrent.TimeUnit.MINUTES; 26 import static java.util.concurrent.TimeUnit.NANOSECONDS; 27 import static java.util.concurrent.TimeUnit.SECONDS; 28 29 import com.google.common.annotations.Beta; 30 import com.google.common.annotations.GwtCompatible; 31 32 import java.util.concurrent.TimeUnit; 33 34 /** 35 * An object that measures elapsed time in nanoseconds. It is useful to measure 36 * elapsed time using this class instead of direct calls to {@link 37 * System#nanoTime} for a few reasons: 38 * 39 * <ul> 40 * <li>An alternate time source can be substituted, for testing or performance 41 * reasons. 42 * <li>As documented by {@code nanoTime}, the value returned has no absolute 43 * meaning, and can only be interpreted as relative to another timestamp 44 * returned by {@code nanoTime} at a different time. {@code Stopwatch} is a 45 * more effective abstraction because it exposes only these relative values, 46 * not the absolute ones. 47 * </ul> 48 * 49 * <p>Basic usage: 50 * <pre> 51 * Stopwatch stopwatch = Stopwatch.{@link #createStarted createStarted}(); 52 * doSomething(); 53 * stopwatch.{@link #stop stop}(); // optional 54 * 55 * long millis = stopwatch.elapsed(MILLISECONDS); 56 * 57 * log.info("time: " + stopwatch); // formatted string like "12.3 ms"</pre> 58 * 59 * <p>Stopwatch methods are not idempotent; it is an error to start or stop a 60 * stopwatch that is already in the desired state. 61 * 62 * <p>When testing code that uses this class, use 63 * {@link #createUnstarted(Ticker)} or {@link #createStarted(Ticker)} to 64 * supply a fake or mock ticker. 65 * <!-- TODO(kevinb): restore the "such as" --> This allows you to 66 * simulate any valid behavior of the stopwatch. 67 * 68 * <p><b>Note:</b> This class is not thread-safe. 69 * 70 * @author Kevin Bourrillion 71 * @since 10.0 72 */ 73 @Beta 74 @GwtCompatible(emulated = true) 75 public final class Stopwatch { 76 private final Ticker ticker; 77 private boolean isRunning; 78 private long elapsedNanos; 79 private long startTick; 80 81 /** 82 * Creates (but does not start) a new stopwatch using {@link System#nanoTime} 83 * as its time source. 84 * 85 * @since 15.0 86 */ 87 public static Stopwatch createUnstarted() { 88 return new Stopwatch(); 89 } 90 91 /** 92 * Creates (but does not start) a new stopwatch, using the specified time 93 * source. 94 * 95 * @since 15.0 96 */ 97 public static Stopwatch createUnstarted(Ticker ticker) { 98 return new Stopwatch(ticker); 99 } 100 101 /** 102 * Creates (and starts) a new stopwatch using {@link System#nanoTime} 103 * as its time source. 104 * 105 * @since 15.0 106 */ 107 public static Stopwatch createStarted() { 108 return new Stopwatch().start(); 109 } 110 111 /** 112 * Creates (and starts) a new stopwatch, using the specified time 113 * source. 114 * 115 * @since 15.0 116 */ 117 public static Stopwatch createStarted(Ticker ticker) { 118 return new Stopwatch(ticker).start(); 119 } 120 121 /** 122 * Creates (but does not start) a new stopwatch using {@link System#nanoTime} 123 * as its time source. 124 * 125 * @deprecated Use {@link Stopwatch#createUnstarted()} instead. 126 */ 127 @Deprecated 128 Stopwatch() { 129 this(Ticker.systemTicker()); 130 } 131 132 /** 133 * Creates (but does not start) a new stopwatch, using the specified time 134 * source. 135 * 136 * @deprecated Use {@link Stopwatch#createUnstarted(Ticker)} instead. 137 */ 138 @Deprecated 139 Stopwatch(Ticker ticker) { 140 this.ticker = checkNotNull(ticker, "ticker"); 141 } 142 143 /** 144 * Returns {@code true} if {@link #start()} has been called on this stopwatch, 145 * and {@link #stop()} has not been called since the last call to {@code 146 * start()}. 147 */ 148 public boolean isRunning() { 149 return isRunning; 150 } 151 152 /** 153 * Starts the stopwatch. 154 * 155 * @return this {@code Stopwatch} instance 156 * @throws IllegalStateException if the stopwatch is already running. 157 */ 158 public Stopwatch start() { 159 checkState(!isRunning, "This stopwatch is already running."); 160 isRunning = true; 161 startTick = ticker.read(); 162 return this; 163 } 164 165 /** 166 * Stops the stopwatch. Future reads will return the fixed duration that had 167 * elapsed up to this point. 168 * 169 * @return this {@code Stopwatch} instance 170 * @throws IllegalStateException if the stopwatch is already stopped. 171 */ 172 public Stopwatch stop() { 173 long tick = ticker.read(); 174 checkState(isRunning, "This stopwatch is already stopped."); 175 isRunning = false; 176 elapsedNanos += tick - startTick; 177 return this; 178 } 179 180 /** 181 * Sets the elapsed time for this stopwatch to zero, 182 * and places it in a stopped state. 183 * 184 * @return this {@code Stopwatch} instance 185 */ 186 public Stopwatch reset() { 187 elapsedNanos = 0; 188 isRunning = false; 189 return this; 190 } 191 192 private long elapsedNanos() { 193 return isRunning ? ticker.read() - startTick + elapsedNanos : elapsedNanos; 194 } 195 196 /** 197 * Returns the current elapsed time shown on this stopwatch, expressed 198 * in the desired time unit, with any fraction rounded down. 199 * 200 * <p>Note that the overhead of measurement can be more than a microsecond, so 201 * it is generally not useful to specify {@link TimeUnit#NANOSECONDS} 202 * precision here. 203 * 204 * @since 14.0 (since 10.0 as {@code elapsedTime()}) 205 */ 206 public long elapsed(TimeUnit desiredUnit) { 207 return desiredUnit.convert(elapsedNanos(), NANOSECONDS); 208 } 209 210 private static TimeUnit chooseUnit(long nanos) { 211 if (DAYS.convert(nanos, NANOSECONDS) > 0) { 212 return DAYS; 213 } 214 if (HOURS.convert(nanos, NANOSECONDS) > 0) { 215 return HOURS; 216 } 217 if (MINUTES.convert(nanos, NANOSECONDS) > 0) { 218 return MINUTES; 219 } 220 if (SECONDS.convert(nanos, NANOSECONDS) > 0) { 221 return SECONDS; 222 } 223 if (MILLISECONDS.convert(nanos, NANOSECONDS) > 0) { 224 return MILLISECONDS; 225 } 226 if (MICROSECONDS.convert(nanos, NANOSECONDS) > 0) { 227 return MICROSECONDS; 228 } 229 return NANOSECONDS; 230 } 231 232 private static String abbreviate(TimeUnit unit) { 233 switch (unit) { 234 case NANOSECONDS: 235 return "ns"; 236 case MICROSECONDS: 237 return "\u03bcs"; // μs 238 case MILLISECONDS: 239 return "ms"; 240 case SECONDS: 241 return "s"; 242 case MINUTES: 243 return "min"; 244 case HOURS: 245 return "h"; 246 case DAYS: 247 return "d"; 248 default: 249 throw new AssertionError(); 250 } 251 } 252 }